package com.kujie.common.modbus;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import lombok.extern.slf4j.Slf4j;

/**
 * modbus通讯工具类,采用modbus4j实现
 *
 * @author jyy
 * @date 2025-02-07
 */
@Slf4j
public class Modbus4jUtils {
    /**
     * 工厂。
     */
    static ModbusFactory modbusFactory;
    static ModbusMaster m;

    static {
        modbusFactory = new ModbusFactory();
    }

    /**
     * 获取master
     *
     * @return ModbusMaster
     * @throws ModbusInitException
     */
    public static ModbusMaster getMaster_bf(String host, int port) throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(host);
        params.setPort(port);
        // RTU 协议
        // modbusFactory.createRtuMaster(wapper);

        // UDP 协议
        // modbusFactory.createUdpMaster(params);

        // ASCII 协议
        // modbusFactory.createAsciiMaster(wrapper);

        // TCP 协议
        ModbusMaster master = modbusFactory.createTcpMaster(params, false);
        master.init();
        return master;
    }


    /**
     * 获取master
     *
     * @param host 主机地址
     * @param port 端口
     * @return ModbusMaster
     */
    public static ModbusMaster getMaster(String host, int port) {
        if (m == null) {
            IpParameters params = new IpParameters();
            params.setHost(host);
            params.setPort(port);

            // TCP 协议
            m = modbusFactory.createTcpMaster(params, true);
            try {
                //设置超时时间
//                m.setTimeout(1000);
                //设置重连次数
                m.setRetries(3);
                //初始化
                m.init();
            } catch (ModbusInitException e) {
                e.printStackTrace();
            }
        }
        return m;
    }

    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId slaveId
     * @param offset  位置
     * @return 读取值
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Boolean readCoilStatus(String host, int port, int slaveId, int offset)
        throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId slaveId
     * @param offset  偏移量
     * @return Boolean
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static Boolean readInputStatus(String host, int port, int slaveId, int offset)
        throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 02 Input Status
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
        Boolean value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     *
     * @param slaveId  slave Id
     * @param offset   位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return Number
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Number readHoldingRegister(String host, int port, int slaveId, int offset, int dataType)
        throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId  slaveId
     * @param offset   位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static Number readInputRegisters(String host, int port, int slaveId, int offset, int dataType)
        throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 批量读取使用方法
     *
     * @param host 主机地址
     * @param port 端口
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static void batchRead(String host, int port) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        BatchRead<String> batch = new BatchRead<>();

        batch.addLocator("0", BaseLocator.holdingRegister(1, 1, DataType.TWO_BYTE_INT_SIGNED));
        batch.addLocator("1", BaseLocator.inputStatus(1, 0));

        ModbusMaster master = getMaster(host, port);

        batch.setContiguousRequests(false);
        BatchResults<String> results = master.send(batch);
        System.out.println(results);
        System.out.println(results.getValue("0"));
        System.out.println(results.getValue("1"));
    }

    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            String host = "192.168.0.243";
            int port = 502;
            // 01测试
//            Boolean v011 = readCoilStatus(host, port, slaveId, 0);
//            Boolean v012 = readCoilStatus(host, port, slaveId, 1);
//            Boolean v013 = readCoilStatus(host, port, slaveId, 6);
//            System.out.println("v011:" + v011);
//            System.out.println("v012:" + v012);
//            System.out.println("v013:" + v013);
            // 02测试
//            Boolean v021 = readInputStatus(host, port, slaveId, 0);
//            Boolean v022 = readInputStatus(host, port, slaveId, 1);
//            Boolean v023 = readInputStatus(host, port, slaveId, 2);
//            System.out.println("v021:" + v021);
//            System.out.println("v022:" + v022);
//            System.out.println("v023:" + v023);


            // 假设 readHoldingRegister 是一个自定义方法，用于从Modbus从设备读取保持寄存器的值
//            // 高16位，即AB
//            Number reg10Value = readHoldingRegister(host, port, 3, 10, DataType.TWO_BYTE_INT_UNSIGNED);
//            // 低16位，即CD
//            Number reg11Value = readHoldingRegister(host, port, 3, 11, DataType.TWO_BYTE_INT_UNSIGNED);
//
//            // 创建一个ByteBuffer来存储32位整数
//            ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
//
//            // 将寄存器10的值作为高16位放入ByteBuffer
//            buffer.putShort(reg10Value.shortValue());
//
//            // 将寄存器11的值作为低16位放入ByteBuffer
//            buffer.putShort(reg11Value.shortValue());
//
//            // 重置ByteBuffer的位置，以便从开始读取
//            buffer.flip();
//
//            // 从ByteBuffer中读取浮点数
//            float floatValue = buffer.getFloat();
//
//            // 输出合并后的浮点数
//            System.out.println("Combined float value: " + floatValue);

            Number reg11Value = readHoldingRegister("192.168.0.242", 502, 2, 101, DataType.TWO_BYTE_INT_UNSIGNED);
            log.info("reg11Value:" + reg11Value);

            // 04测试
//            Number v041 = readInputRegisters(host, port, slaveId, 0, DataType.FOUR_BYTE_FLOAT);
//            Number v042 = readInputRegisters(host, port, slaveId, 2, DataType.FOUR_BYTE_FLOAT);
//            System.out.println("v041:" + v041);
//            byte[] bs = ByteUtil.getBytes(v041.floatValue());
//            ByteUtil.getBytes(v041.intValue());
//            BitUtil.getBitValue(bs[0], 2);
//
//            System.out.println("v042:" + v042);
            // 批量读取
//            batchRead(host, port);

        } catch (Exception e) {
            log.error(e.toString());
        }
    }
}
